package br.com.intelsys.iflow.jbpm;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Query;
import org.hibernate.Session;
import org.jbpm.JbpmContext;
import org.jbpm.graph.def.ProcessDefinition;
import org.jbpm.graph.exe.ProcessInstance;

import br.com.intelsys.iflow.IFlow;
import br.com.intelsys.iflow.IFlowInstance;

public abstract class IFlowJbpmWrapper implements IFlow {
	JbpmContext jbpmContext;
	private static final Log log = LogFactory.getLog(IFlowJbpmWrapper.class);

	abstract boolean openContext();
	
	public IFlowInstance createInstance(String processName) {
		IFlowInstance retTmp;
		if (this.openContext()) {
			log.debug("Criando uma nova instancia para o processo: "+processName);
			retTmp = new IFlowInstanceJbpm(jbpmContext, jbpmContext.newProcessInstance(processName));
		} else {
			retTmp = null;
		}
		return(retTmp);
	}

	public IFlowInstance getInstance(long processInstanceId) {
		IFlowInstance retTmp;
		if (this.openContext()) {
			log.debug("Recuperando uma instancia do IFlow com o id: "+processInstanceId);
			ProcessInstance pi = jbpmContext.getProcessInstance(processInstanceId);
			log.debug("ProcessInstance carregado? "+(pi != null));
			retTmp = new IFlowInstanceJbpm(jbpmContext, pi);
			log.debug("IFlowInstance carregado? "+(retTmp != null));
		} else {
			retTmp = null;
		}
		return(retTmp);
	}
	
	public List <IFlowInstance> getInstanceListByStateName(String processDefinitionName, String stateName) {
		List <String> stateNameList = new ArrayList <String> (1);
		stateNameList.add(stateName);
		return(this.getInstanceListByStateName(processDefinitionName, stateNameList));
	}
	
	public List <IFlowInstance> getInstanceListByStateName(String processDefinitionName, List <String> stateNameList) {
		List <IFlowInstance> instList;
		if (this.openContext()) {
			Session sess = jbpmContext.getSession();
			Query q = sess.getNamedQuery("br.com.intelsys.iflow.jbpm.findAllRunningProcessInstancesWithProcessNameAndNodeName");
			q.setString("processDefinitionName", processDefinitionName);
			q.setParameterList("nodeNameList", stateNameList);
			List <ProcessInstance> lProcInst = q.list();
			if (lProcInst != null) {
				instList = new ArrayList <IFlowInstance> (lProcInst.size());
				for (ProcessInstance pi : lProcInst) {
					instList.add(new IFlowInstanceJbpm(jbpmContext, pi));
				}
			} else {
				instList = null;
			}
		} else {
			instList = null;
		}
		return(instList);
	}
	
	/**
	 * Carrega uma definicao de processo para este contexto baseado em um XML.
	 * 
	 * Este eh um apelido criado para o loadProcessDefinitionXml() com o objetivo de facilitar a IOC.
	 * 
	 * @param resourceName Caminho (resource) para o XML.
	 */
	public void setProcessDefinitionXml(String resourceName) {
		this.loadProcessDefinitionXml(resourceName);
	}
	
	/**
	 * Carrega uma definicao de processo para este contexto baseado em um XML.
	 * 
	 * @param resourceName Caminho (resource) para o XML.
	 */
	public void loadProcessDefinitionXml(String resourceName) {
		if (this.openContext()) {
			log.debug("Carregando um novo ProcessDefinition baseado em um XML no resource: " + resourceName);
			jbpmContext.deployProcessDefinition(ProcessDefinition.parseXmlResource(resourceName));
		}
	}

	public void closeContext() {
		if (this.jbpmContext != null) {
			log.debug("Fechando um contexto jBPM");
			this.jbpmContext.close();
			this.jbpmContext = null;
		}
	}
}
